package com.fzk.server;

import com.fzk.core.FileStoreService;
import com.fzk.env.conf.DataConf;
import com.fzk.env.conf.ServerConf;
import com.fzk.ha.HAService;
import com.fzk.log.Logger;
import com.fzk.router.RouterHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

import java.io.Closeable;
import java.io.IOException;
import java.util.Map;

/**
 * 基于netty的http协议服务器
 *
 * @author fzk
 * @datetime 2023-02-13 20:38:42
 */
public class NettyHttpServer implements Closeable {
    public final NioEventLoopGroup bossGroup;
    public final NioEventLoopGroup workerGroup;
    public final ServerConf serverConf;
    public final DataConf dataConf;
    public final FileStoreService fileService;
    public final HAService haService;
    public static final String webSocketPath = "/fs/websocket";

    public NettyHttpServer(ServerConf serverConf, DataConf dataConf) throws IOException {
        this.serverConf = serverConf;
        this.dataConf = dataConf;
        // 1.创建两个EventLoopGroup对象
        // boss线程组：用于服务端接受客户端连接，一般1个Acceptor线程就够了
        this.bossGroup = new NioEventLoopGroup(1);
        // worker线程组：用于Channel的IO操作和业务处理，默认线程数=CPU*2
        this.workerGroup = new NioEventLoopGroup();

        // 2.新建文件存储服务
        this.fileService = new FileStoreService(this);

        // 3.新建ha service，还未启动
        this.haService = new HAService(this, fileService, serverConf, dataConf);
    }

    public void start() throws IOException, InterruptedException {
        //final SslContext sslCtx = ServerUtil.buildSslContext();
        final Map<String, Object> map = Map.of("fileService", fileService, "serverConf", serverConf, "dataConf", dataConf);
        RouterHandler router = new RouterHandler(map);// 路由

        // 2.创建并配置服务端启动器
        ServerBootstrap b = new ServerBootstrap();
        b.option(ChannelOption.SO_BACKLOG, 1024);
        b.group(bossGroup, workerGroup) // 配置主从线程组
                .channel(NioServerSocketChannel.class) // 配置要使用的Channel类
//                    .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline();
                        //if (sslCtx != null) {
                        //    p.addLast(sslCtx.newHandler(ch.alloc()));
                        //}
                        // 3.设置接入服务端的SocketChannel的处理器管道
                        // 向管道内加入HTTP协议的编码/解码器
                        p.addLast(new HttpServerCodec());
                        p.addLast(new HttpObjectAggregator(serverConf.getHttpMaxContentSize()));// 限制HTTP请求内容最大100MB
                        p.addLast(new ChunkedWriteHandler());

                        // websocket 协议
                        p.addLast(new WebSocketServerCompressionHandler());// 只处理ws协议扩展
                        p.addLast(new WebSocketServerProtocolHandler(webSocketPath, null, true, serverConf.getHttpMaxContentSize()));
//                            p.addLast(new WebSocketIndexPageHandler(webSocketPath));
                        p.addLast(new WebSocketFrameHandler(fileService));

                        p.addLast(router);// 自定义handler
                        p.addLast(new UploadHandler(fileService));// 文件上传处理器
                        p.addLast(new ExceptionHandler());// 最后异常处理器兜底
                    }
                });

        // 4.绑定端口，同步等待
        Channel ch = b.bind(serverConf.getPort()).sync().channel();

        Logger.info("Netty server start successful. Open your web browser and navigate to " +
                "http" + "://127.0.0.1:" + serverConf.getPort() + '/');

        // 5.启动HAService
        if (serverConf.isOpenHaService()) {
            this.haService.start();
        }

        // 6.同步等待直至监听服务端socket端口关闭
//            ch.closeFuture().sync();
        ch.closeFuture().addListener((gt) -> {
            Logger.info("ServerSocketChannel closed");
        });
    }


    @Override
    public void close() throws IOException {
        // 7.优雅的关闭两个线程组
        try {
            Logger.info("Netty server will shutdown");
            bossGroup.shutdownGracefully().sync();
            Logger.info("Netty server bossGroup shutdown successfully");
            workerGroup.shutdownGracefully().sync();
            Logger.info("Netty server workerGroup shutdown successfully");
            Logger.info("Netty server shutdown successfully");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
